package com.block.product.service.union.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


public class AESUtil {

    private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);

    public static final Charset CHARSET = Charset.forName("utf-8");

    public static final byte keyStrSzie = 16;

    public static final String ALGORITHM = "AES";

    public static final String AES_CBC_NOPADDING = "AES/CBC/NoPadding";


    /**
     * AES/CBC/NoPadding encrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param secretKeyBytes
     * @param intVectorBytes
     * @param input
     * @return
     */
    public static byte[] encryptCBCNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);
            int inputLength = input.length;
            int srcLength;

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            int blockSize = cipher.getBlockSize();
            byte[] srcBytes;
            if (0 != inputLength % blockSize) {
                srcLength = inputLength + (blockSize - inputLength % blockSize);
                srcBytes = new byte[srcLength];
                System.arraycopy(input, 0, srcBytes, 0, inputLength);
            } else {
                srcBytes = input;
            }

            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(srcBytes);
            return encryptBytes;
        } catch (Exception e) {
            logger.error("加密出现异常", e);
        }
        return null;
    }


    /**
     * AES/CBC/NoPadding decrypt
     * 16 bytes secretKeyStr
     * 16 bytes intVector
     *
     * @param secretKeyBytes
     * @param intVectorBytes
     * @param input
     * @return
     */
    public static byte[] decryptCBCNoPadding(byte[] secretKeyBytes, byte[] intVectorBytes, byte[] input) {
        try {
            IvParameterSpec iv = new IvParameterSpec(intVectorBytes);
            SecretKey secretKey = new SecretKeySpec(secretKeyBytes, ALGORITHM);

            Cipher cipher = Cipher.getInstance(AES_CBC_NOPADDING);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            byte[] encryptBytes = cipher.doFinal(input);
            return encryptBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static byte[] encode(String secretStr, String inputStr) {
        if (keyStrSzie != secretStr.length()) {
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);
        byte[] inputBytes = inputStr.getBytes(CHARSET);

        byte[] outputBytes = encryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     * 并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     * 转换成 base64 字符串返回。
     *
     * @param secretStr
     * @param inputStr
     * @return
     */
    public static String strEncodBase64(String secretStr, String inputStr) {
        String base64Str = Base64.encodeBase64String(encode(secretStr, inputStr));
        return base64Str;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     *
     * @param secretStr
     * @param inputBytes
     * @return
     */
    public static byte[] decode(String secretStr, byte[] inputBytes) {
        if (keyStrSzie != secretStr.length()) {
            return null;
        }
        byte[] secretKeyBytes = secretStr.getBytes(CHARSET);
        byte[] ivBytes = Arrays.copyOfRange(secretKeyBytes, 0, 16);

        byte[] outputBytes = decryptCBCNoPadding(secretKeyBytes, ivBytes, inputBytes);
        return outputBytes;
    }

    /**
     * 用 AES 算法加密 inputStr。
     * 使用 secretStr 作为 key，secretStr 的前 16 个字节作为 iv。
     * 并对加密后的字节数组调用 sun.misc.BASE64Encoder.encode 方法，
     * 转换成 base64 字符串返回。
     * <p>
     * （仅作为测试用途，具体加密流程以接口文档为准）
     *
     * @param secretStr
     * @param inputStr
     * @return
     * @throws IOException
     */
    public static String base64StrDecode(String secretStr, String inputStr) {
        byte[] inputBytes;
        inputBytes = Base64.decodeBase64(inputStr);
        String outputStr = new String(decode(secretStr, inputBytes), CHARSET);
        System.out.println("base64Decode > base64 decrypt " + outputStr);
        return outputStr;
    }


    /**
     * 用 AES 算法解密
     * <p>
     * （仅作为测试用途，具体加密流程以接口文档为准）
     *
     * @param secretStr
     * @param inputStr
     * @return
     * @throws IOException
     */
    public static String decodeParams(String secretStr, String inputStr) {
        byte[] inputBytes;
        inputBytes = Base64.decodeBase64(hexToByte(inputStr));
        String outputStr = new String(decode(secretStr, inputBytes), CHARSET);
        System.out.println("hexToByte > base64Decode > base64 decrypt " + outputStr);
        return outputStr;
    }

    /**
     * hex转byte数组
     *
     * @param hex
     * @return
     */
    public static byte[] hexToByte(String hex) {
        int m = 0, n = 0;
        int byteLen = hex.length() / 2; // 每两个字符描述一个字节
        byte[] ret = new byte[byteLen];
        for (int i = 0; i < byteLen; i++) {
            m = i * 2 + 1;
            n = m + 1;
            int intVal = Integer.decode("0x" + hex.substring(i * 2, m) + hex.substring(m, n));
            ret[i] = Byte.valueOf((byte) intVal);
        }
        return ret;
    }

    /**
     * byte数组转化为16进制字符串
     *
     * @param bytes
     * @return
     */
    public static String byteToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex = Integer.toHexString(bytes[i]);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }

    /**
     * AES 加密
     *
     * @param content 待加密内容
     * @param key     秘钥
     * @return String 密文
     */
    public static String encrypt(String content, String key) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
            byte[] bytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(bytes);
        } catch (Exception e) {
            throw new RuntimeException("AES加密出现异常", e);
        }
    }

    /**
     * AES 加密
     *
     * @param content 待加密内容
     * @param key     秘钥
     * @return String 密文
     */
    public static String encryptByCBC(String content, String key, String iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            byte[] ivBytes = iv.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(ivBytes));
            byte[] bytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(bytes);
        } catch (Exception e) {
            throw new RuntimeException("AES加密出现异常", e);
        }
    }

    /**
     * AES 解密
     *
     * @param encryptBytes 密文
     * @param key          秘钥
     * @return String 明文
     */
    public static String decrypt(String encryptBytes, String key) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
            byte[] decryptBytes = cipher.doFinal(Base64.decodeBase64(encryptBytes));
            return new String(decryptBytes);
        } catch (Exception e) {
            throw new RuntimeException("AES解密出现异常", e);
        }
    }

    public static void main(String[] args) {
        String key = "77f522062e144b0d";

        Map<String, Object> userAttr = new HashMap<>();
        //userAttr.put("mobileNo", "421202199501233266");
        userAttr.put("code", "0");
        userAttr.put("msg", "成功");
        //userAttr.put("name", "张三");

        String userAttrStr = JSONObject.toJSONString(userAttr);

        /*String enUserAttr =  encodeParams(key, userAttrStr);
        System.out.println("encodedStr = " + enUserAttr);
        System.out.println("decodedStr = " + new String(decodeParams(key,enUserAttr)));*/

        //加密
        String enUserAttr = strEncodBase64(key, userAttrStr);
        //解密
        String deUserAttr = base64StrDecode(key, enUserAttr);
        System.out.println("encodedStr = " + enUserAttr);
        System.out.println("decodedStr = " + deUserAttr);
        Object json = JSON.parse(deUserAttr);
        System.out.println(json);
    }
    
    /*public static void main(String[] args) {
        String key = "77f522062e144b0d";

        Map<String, Object> userAttr = new HashMap<>();
        userAttr.put("code", "0");
        userAttr.put("msg", "成功");


      //  String key = "3FADAE9950B216AF";

      //  Map<String, Object> userAttr = new TreeMap<>();
      //  userAttr.put("mobileNo", "421202199501233266");
      //  userAttr.put("mobile", "13701234567");
      //  userAttr.put("name", "张三");
        //待加密数据
        String userAttrStr = JSONObject.toJSONString(userAttr);
        System.out.println("userAttrStr = " + userAttrStr);
      //  System.out.println("userAttrStr = " + userAttrStr.getBytes().length);
        //加密
      //  String enUserAttr =  new String(encode(key, userAttrStr));//strEncodBase64(key, userAttrStr);
      //  System.out.println("encodedStr1 = " + enUserAttr.getBytes().length);
        String enUserAttr2 =  strEncodBase64(key, userAttrStr);
        System.out.println("encodedStr2 = " + enUserAttr2);
        //解密

        String deUserAttr = base64StrDecode(key, enUserAttr2);
        Object abc = JSON.parse(deUserAttr);//JSONObject.parseObject(deUserAttr);
        System.out.println("deUserAttr = " + abc);
      //  System.out.println("deUserAttr = " + deUserAttr.getBytes().length);
       }*/

}
